﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Koala.Pro.Common
{
    public static class TypeExtensions
    {
        private static readonly Dictionary<Type, string> _builtInTypeNames = new Dictionary<Type, string>
    {
        {
            typeof(bool),
            "bool"
        },
        {
            typeof(byte),
            "byte"
        },
        {
            typeof(char),
            "char"
        },
        {
            typeof(decimal),
            "decimal"
        },
        {
            typeof(double),
            "double"
        },
        {
            typeof(float),
            "float"
        },
        {
            typeof(int),
            "int"
        },
        {
            typeof(long),
            "long"
        },
        {
            typeof(object),
            "object"
        },
        {
            typeof(sbyte),
            "sbyte"
        },
        {
            typeof(short),
            "short"
        },
        {
            typeof(string),
            "string"
        },
        {
            typeof(uint),
            "uint"
        },
        {
            typeof(ulong),
            "ulong"
        },
        {
            typeof(ushort),
            "ushort"
        },
        {
            typeof(void),
            "void"
        }
    };

        public static bool IsNullableType(this Type type)
        {
            if (type != null && type.IsGenericType)
            {
                return type.GetGenericTypeDefinition() == typeof(Nullable<>);
            }
            return false;
        }

        public static Type GetNonNullableType(this Type type)
        {
            if (!type.IsNullableType())
            {
                return type;
            }
            return type.GetGenericArguments()[0];
        }

        public static Type GetUnNullableType(this Type type)
        {
            if (type.IsNullableType())
            {
                return new NullableConverter(type).UnderlyingType;
            }
            return type;
        }

        public static string GetDescription(this Type type, bool inherit = true)
        {
            DescriptionAttribute attribute = type.GetAttribute<DescriptionAttribute>(inherit);
            if (attribute != null)
            {
                return attribute.Description;
            }
            return type.FullName;
        }

        public static string GetDescription(this MemberInfo member, bool inherit = true)
        {
            DescriptionAttribute attribute = member.GetAttribute<DescriptionAttribute>(inherit);
            if (attribute != null)
            {
                return attribute.Description;
            }
            DisplayNameAttribute attribute2 = member.GetAttribute<DisplayNameAttribute>(inherit);
            if (attribute2 != null)
            {
                return attribute2.DisplayName;
            }
            DisplayAttribute attribute3 = member.GetAttribute<DisplayAttribute>(inherit);
            if (attribute3 != null)
            {
                return attribute3.Name;
            }
            return member.Name;
        }

        public static bool HasAttribute<T>(this MemberInfo memberInfo, bool inherit = true) where T : Attribute
        {
            return memberInfo.IsDefined(typeof(T), inherit);
        }

        public static T GetAttribute<T>(this MemberInfo memberInfo, bool inherit = true) where T : Attribute
        {
            return memberInfo.GetCustomAttributes(typeof(T), inherit).FirstOrDefault() as T;
        }

        public static T[] GetAttributes<T>(this MemberInfo memberInfo, bool inherit = true) where T : Attribute
        {
            return memberInfo.GetCustomAttributes(typeof(T), inherit).Cast<T>().ToArray();
        }

        public static bool IsEnumerable(this Type type)
        {
            if (type == typeof(string))
            {
                return false;
            }
            return typeof(IEnumerable).IsAssignableFrom(type);
        }

        public static bool IsGenericAssignableFrom(this Type genericType, Type type)
        {
            genericType.CheckNotNull("genericType");
            type.CheckNotNull("type");
            if (!genericType.IsGenericType)
            {
                throw new ArgumentException("该功能只支持泛型类型的调用，非泛型类型可使用 IsAssignableFrom 方法。");
            }
            List<Type> list = new List<Type> { type };
            if (genericType.IsInterface)
            {
                list.AddRange(type.GetInterfaces());
            }
            foreach (Type item in list)
            {
                Type type2 = item;
                while (type2 != null)
                {
                    if (type2.IsGenericType)
                    {
                        type2 = type2.GetGenericTypeDefinition();
                    }
                    if (type2.IsSubclassOf(genericType) || type2 == genericType)
                    {
                        return true;
                    }
                    type2 = type2.BaseType;
                }
            }
            return false;
        }

        public static bool IsGenericFor(this Type type, Type forArgType)
        {
            if (!type.IsGenericType)
            {
                return false;
            }
            Type[] genericArguments = type.GetGenericArguments();
            if (genericArguments.Length == 0)
            {
                return false;
            }
            _ = genericArguments[0];
            _ = forArgType.IsGenericType;
            throw new NotImplementedException();
        }

        public static bool IsBaseOn(this Type type, Type baseType)
        {
            if (baseType.IsGenericTypeDefinition)
            {
                return baseType.IsGenericAssignableFrom(type);
            }
            return baseType.IsAssignableFrom(type);
        }

        public static bool IsBaseOn<TBaseType>(this Type type)
        {
            Type typeFromHandle = typeof(TBaseType);
            return type.IsBaseOn(typeFromHandle);
        }

        public static bool IsAsync(this MethodInfo method)
        {
            if (!(method.ReturnType == typeof(Task)))
            {
                if (method.ReturnType.IsGenericType)
                {
                    return method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>);
                }
                return false;
            }
            return true;
        }

        public static bool IsOverridden(this MethodInfo method)
        {
            return method.GetBaseDefinition().DeclaringType != method.DeclaringType;
        }

        public static bool IsVirtual(this PropertyInfo property)
        {
            MethodInfo methodInfo = property.GetAccessors().FirstOrDefault();
            if (methodInfo == null)
            {
                return false;
            }
            if (methodInfo.IsVirtual)
            {
                return !methodInfo.IsFinal;
            }
            return false;
        }

        public static string GetFullNameWithModule(this Type type)
        {
            return type.FullName + "," + type.Module.Name.Replace(".dll", "").Replace(".exe", "");
        }

        public static string ShortDisplayName(this Type type)
        {
            return type.DisplayName(fullName: false);
        }

        public static string DisplayName(this Type type, bool fullName = true)
        {
            StringBuilder stringBuilder = new StringBuilder();
            ProcessType(stringBuilder, type, fullName);
            return stringBuilder.ToString();
        }

        private static void ProcessType(StringBuilder builder, Type type, bool fullName)
        {
            string value;
            if (type.IsGenericType)
            {
                Type[] genericArguments = type.GetGenericArguments();
                ProcessGenericType(builder, type, genericArguments, genericArguments.Length, fullName);
            }
            else if (type.IsArray)
            {
                ProcessArrayType(builder, type, fullName);
            }
            else if (_builtInTypeNames.TryGetValue(type, out value))
            {
                builder.Append(value);
            }
            else if (!type.IsGenericParameter)
            {
                builder.Append(fullName ? type.FullName : type.Name);
            }
        }

        private static void ProcessArrayType(StringBuilder builder, Type type, bool fullName)
        {
            Type type2 = type;
            while (type2.IsArray)
            {
                type2 = type2.GetElementType();
            }
            ProcessType(builder, type2, fullName);
            while (type.IsArray)
            {
                builder.Append('[');
                builder.Append(',', type.GetArrayRank() - 1);
                builder.Append(']');
                type = type.GetElementType();
            }
        }

        private static void ProcessGenericType(StringBuilder builder, Type type, Type[] genericArguments, int length, bool fullName)
        {
            int num = (type.IsNested ? type.DeclaringType.GetGenericArguments().Length : 0);
            if (fullName)
            {
                if (type.IsNested)
                {
                    ProcessGenericType(builder, type.DeclaringType, genericArguments, num, fullName);
                    builder.Append('+');
                }
                else
                {
                    builder.Append(type.Namespace);
                    builder.Append('.');
                }
            }
            int num2 = type.Name.IndexOf('`');
            if (num2 <= 0)
            {
                builder.Append(type.Name);
                return;
            }
            builder.Append(type.Name, 0, num2);
            builder.Append('<');
            for (int i = num; i < length; i++)
            {
                ProcessType(builder, genericArguments[i], fullName);
                if (i + 1 != length)
                {
                    builder.Append(',');
                    if (!genericArguments[i + 1].IsGenericParameter)
                    {
                        builder.Append(' ');
                    }
                }
            }
            builder.Append('>');
        }
    }

}
